package cn.tedu._10springaop.aop;
/**
 * Aspect 注解
 * 1.标识该类为切面类
 * 2.当调用目标方法是时，框架会检查是否定义了切面类
 * 3.如果定义了切面类，则在改类中找切点方法;
 */

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;


import java.util.Arrays;

@Aspect
@Component
public class LogAspect {

    /**
     * Pointcut 注解 定义切入点表达式的方法
     */

    //1.拦截指定的1个方法
    //@Pointcut(value="execution(public int cn.tedu._10springaop.aop.CalculatorImpl.add(..))")
    //2.拦截多个指定方法
    //@Pointcut(value="execution(public int cn.tedu._10springaop.aop.CalculatorImpl.add(..))"
    // || "execution(public int cn.tedu._10springaop.aop.CalculatorImpl.sub(..))" )
    //3.拦截指定类中所有的方法
    //@Pointcut(value="execution(public int cn.tedu._10springaop.aop.CalculatorImpl.*(..))"
    //4.拦截所有方法
    //@Pointcut(value="execution(* *..*.*(..)) && !within(org.springframework..*) && !within(org.springframework.boot..*)")
    //5.Bean（）：拦截指定的IOC容器中Bean中定义的所有方法，不能精确到某一个方法
    //@Pointcut(value="Bean(CalculatorImpl)")
    //6.within(b包名，类名)
    //@Pointcut(value="within(cn.tedu._10springaop.aop.CalculatorImpl)")

    @Pointcut(value="execution(public int cn.tedu._10springaop.aop.CalculatorImpl.*(..))")
    public void doTime(){}

    /**
     * Before 注解
     * 前置通知，在目标方法执行前执行；
     */
    @Before(value = "doTime()")
    public void beforMethod(JoinPoint jionPoint){
        /*
        连接点参数JoinPoint中获取：目标的方法名称和参数列表
         */
        String name=jionPoint.getSignature().getName();
        String args= Arrays.toString(jionPoint.getArgs());
        System.out.println(name+"方法开始了，参数为"+args);
    }

    /**
     * AfterReturning 注解
     * 1.返回通知，在目标方法正常返回后执行
     * 2.返回通知有权限获取目标方法的返回值
     */
    @AfterReturning(value = "doTime()",returning="r")
    public void afterReturningMethod(JoinPoint joinPoint,Object r){
        String name=joinPoint.getSignature().getName();
        System.out.println("[返回通知]："+name+"方法正常返回了，结果为："+r);
    }



    /**
     * After 注解
     * 1.后置通知，在目标方法执行后执行
     * 2.目标方法正常结束和异常结束后都会执行
     * 3.后置通知没有权限获取目标方法的返回值
     *
     */
    @After(value = "doTime()")
    public void afterMethod(JoinPoint joinPoint){
        String name=joinPoint.getSignature().getName();
        System.out.println("[后置通知]:"+name+"方法结束了！");

    }

    /**
     * AfterThrowing 注解
     * 1，异常通知，在目标方法异常结束后执行
     * 2。异常通知有权限获取到目标方法抛出的异常对象
     */
    @AfterThrowing(value="doTime()",throwing="ex")
    public void afterThrowingMethod(JoinPoint joinPoint,Throwable ex){
        String name =joinPoint.getSignature().getName();
        System.out.println("[异常通知]："+name+"方法异常结束了，异常信息为："+ex.getMessage());


    }

    /**
     * Around 注解
     * 1.环绕通知，等价以上4种通知的组合，Before + After + AfterReturning + AfterThrowing
     * 2.环绕通知有权限获取目标方法返回值
     * 3.环绕通知没有权限获取目标方法抛出的异常对象
     * 通常通过try...catch..finally来处理异常
     */

    @Around(value = "doTime()")
    public Object aroundMethod(ProceedingJoinPoint joinPoint){
        Object result=null;
        try{

        System.out.println("[环绕通知-前置通知]");
        result=joinPoint.proceed();
            System.out.println("[环绕通知-返回通知]");
        }catch(Throwable ex){
            System.out.println("[环绕通知-异常通知]");
        }finally {
            System.out.println("[环绕通知-后置通知]");

        }
        return result;
    }



}
